package com.mtao.utils;

import com.mtao.constant.SecurityConstants;
import com.mtao.exception.BadRequestException;
import com.mtao.entity.SysUser;
import io.jsonwebtoken.*;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import java.util.*;
import java.util.stream.Collectors;


@Data
@Component
@ConfigurationProperties(prefix = "jwt")
public class JwtUtils {


    //使用@ConfigurationProperties注解可以读取配置文件中的信息，只要在 Bean 上添加上了这个注解，
    // 指定好配置文件中的前缀，那么对应的配置文件数据就会自动填充到 Bean 的属性中
    private long expire= 60 * 60 * 2L;
    private String secret;
    private String header;

    /**
     * @Description: 校验token是否有效
     * @Param: [token]
     * @return: boolean
     * @Author: starao
     * @Date: 2021/11/27
     */
    public boolean verifyToken(String token) {
        try {
            Claims claims = getClaimsByToken(token);
            Date expiration = claims.getExpiration();
            if (expiration.before(new Date())) {
                System.out.println("Token已过期。");
                return false;
            }
            return true;
        } catch (ExpiredJwtException e) {
            throw new BadRequestException(e.getMessage());
        }
    }

    /**
     * 生成JWT
     *
     * @param
     * @return
     */
    public String createJwt(SysUser user, List<String> roles, Boolean isRemember) {
        Date nowDate = new Date();
        Date expireDate = new Date(nowDate.getTime() + 1000 * expire);
        return Jwts.builder()
                //  生成签证信息
                .setHeaderParam("typ", "JWT")
                .signWith(SignatureAlgorithm.HS512, secret)
                .setSubject(user.getUsername())
                //  角色
                .claim(SecurityConstants.TOKEN_ROLE_CLAIM, roles)
                .claim(SecurityConstants.TOKEN_USER_CLAIM, user.getUsername())
                .claim(SecurityConstants.TOKEN_USER_ID, user.getId())
                //  JWT主体
                .setIssuer(SecurityConstants.TOKEN_ISSUER)
                //  签发时间
                .setIssuedAt(nowDate)
                .setAudience(SecurityConstants.TOKEN_AUDIENCE)
                //  设置有效时间 7天过期
                .setExpiration(expireDate)    //
                .compact();
    }

    /**
     * @Description: 生成refresh_token
     * @Param: [userName]
     * @return: java.lang.String
     * @Author: starao
     * @Date: 2022/10/5
     */
    public String getRefreshToken(String userName) {
        return Jwts.builder()
                .signWith(SignatureAlgorithm.HS512, secret)
                .setSubject(userName)
                .setExpiration(new Date(System.currentTimeMillis() + SecurityConstants.TOKEN_EXPIRATION_REMEMBER_TIME * 1000))
                .compact();
    }

    /**
     * 解析JWT
     *
     * @param jwt
     * @return
     */
    public Claims getClaimsByToken(String jwt) {
        try {
            return Jwts.parser()
                    .setSigningKey(secret)
                    .parseClaimsJws(jwt)
                    .getBody();
        } catch (Exception e) {
            return null;
        }
    }

    /**
     * 判断JWT是否过期
     *
     * @param claims
     * @return
     */
    public boolean isTokenExpired(Claims claims) {
        return claims.getExpiration().before(new Date());
    }

    /**
     * @Description: 根据token获取用户认证信息
     * @Param: [token]
     */
    public Authentication getAuthentication(String token) {
        Claims claims=null;
        try {
            claims = getClaimsByToken(token);
        } catch (ExpiredJwtException e){
            e.printStackTrace();
            claims = e.getClaims();
        }
        //  获取用户角色
        List<String> roles = (List<String>) claims.get(SecurityConstants.TOKEN_ROLE_CLAIM);
        List<SimpleGrantedAuthority> authorities =
                CollectionUtils.isEmpty(roles) ? Collections.singletonList(new SimpleGrantedAuthority("ROLE_USER")) :
                        roles.stream().map(SimpleGrantedAuthority::new).collect(Collectors.toList());

        //  获取用户名
        User principal = new User(claims.getSubject(), "******", authorities);
        return new UsernamePasswordAuthenticationToken(principal,token, authorities);
    }

}

